/*
 *  ifxsql.hpp - Informix dbExpress interfaces
 *
 *    Copyright (c) 2001-2006, Luxena Company. All rights reserved.
 *
 *  Purpose:
 *
 */

#ifndef _IFXSQL_HPP
#define _IFXSQL_HPP

#ifndef __cplusplus
#error This header requires a C++ compiler ...
#endif

#include <sqltypes.h>
#include <sqlstype.h>

#include "cstr.hpp"
#include "basetypes.hpp"
#include "dbxpress.hpp"

#define MAX_ERROR_MSG_LEN 0x7fff

/////////////////////////////////////////////////////////////////////////////
// DLL API declaration

API(SQLResult) getInformixDriver(
	pCHAR pszVendorLib, pCHAR pszResourceFile, ppSQLDriver ppDrv
	);


/////////////////////////////////////////////////////////////////////////////
// CInformixDriver - SQLDriver implementation for Informix

class CInformixDriver : public CUnknownImpl<SQLDriver>, public CTracedImpl
{
private:
	// dbExpress connection options
	INT32 m_cbBlob;           // eDrvBlobSize
	pfCallBack m_pfCallBack;  // eDrvCallBack
	TRACEDesc m_TRACEDesc;    // eDrvCallBackInfo
	                          // eDrvRestrict

public:
	CInformixDriver ();
	INT32 getBlobSize ();

// SQLDriver
public:
	SQLMETHOD getSQLConnection (ppSQLConnection ppConn);
	SQLMETHOD setOption (eSQLDriverOption eDOption, INT32 PropValue);
	SQLMETHOD getOption (eSQLDriverOption eDOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength);
};


/////////////////////////////////////////////////////////////////////////////
// CInformixError - Informix error managing object

class CInformixError : public CTracedImpl
{
protected:
	pCHAR m_pszError;

public:
	CInformixError ();
	virtual ~CInformixError ();
	pCHAR getError ();
	void setError (pCHAR pszError);
	SQLResult checkError (mint sqlcode);
	SQLResult checkError ();
};


/////////////////////////////////////////////////////////////////////////////
// CErroredImpl - implementation for error handling methods

template <class T>
class CErroredImpl : public CUnknownImpl<T>, public CInformixError
{
public:
	SQLMETHOD getErrorMessage (pBYTE pszError);
	SQLMETHOD getErrorMessageLen (pUINT16 puErrorLen);
};


/////////////////////////////////////////////////////////////////////////////
// CInformixConnection - SQLConnection implementation for Informix

struct IInformixConnection : public SQLConnection
{
    METHOD(sqlca_s *) get_sqlca() = 0;
} com_interface;

class CInformixConnection : public CErroredImpl<IInformixConnection>
{
private:
	CInformixDriver *m_pDriver;

	// dbExpress connection options
	BOOL m_bAutoCommit;           // eConnAutoCommit
	                              // eConnBlockingMode
	INT32 m_cbBlob;               // eConnBlobSize
	                              // eConnRoleName
	BOOL m_bWaitOnLocks;          // eConnWaitOnLocks
	int4 m_bCommitRetain;         // eConnCommitRetain
	eXILType m_eTxnIsoLevel;      // eConnTxnIsoLevel
	pCHAR m_pszHandle;            // eConnNativeHandle
	                              // eConnServerVersion
	pfCallBack m_pfCallBack;      // eConnCallBack
	                              // eConnHostName
	                              // eConnDatabaseName
	TRACEDesc m_TRACEDesc;        // eConnCallBackInfo
	                              // eConnObjectMode
	                              // eConnMaxActiveComm
	pCHAR m_pszCharSet;           // eConnServerCharSet
	                              // eConnSqlDialect
	                              // eConnRollbackRetain
	                              // eConnObjectQuoteChar
	                              // eConnConnectionName
	                              // eConnOSAuthentication
	BOOL m_bSupportsTransaction;  // eConnSupportsTransaction
	                              // eConnMultipleTransaction
	                              // eConnServerPort
	BOOL m_bOnLine;               // eConnOnLine
	BOOL m_bTrimChar;             // eConnTrimChar
	_cstr_t m_strQualifiedName;   // eConnQualifiedName
	char *m_pszDatabase;          // eConnCatalogName
	char *m_pszUser;              // eConnSchemaName
	char *m_pszObjectName;        // eConnObjectName
	                              // eConnQuotedObjectName
	                              // eConnCustomInfo
	                              // eConnTimeOut

	bool m_bANSI;

	SQLResult applyWaitOnLocks();
	SQLResult applyTxnIsoLevel(bool bRetainUpdateLocks);

	// saved SQLCA structure
	sqlca_s m_sqlca;

public:

	CInformixConnection ();
	virtual ~CInformixConnection ();
	void setDriver (CInformixDriver *pDriver);

	SQLResult checkCurrent ();
	SQLResult checkDormant (SQLResult sqlresult);
	SQLResult checkAnsi (SQLResult sqlresult);

	BOOL getSupportsTransactions ();
	BOOL isNotInformixSE ();
	INT32 getBlobSize ();
	int4 getCommitRetain ();
	BOOL getTrimChar ();

	void save_sqlca ();

// SQLConnection
public:
	SQLMETHOD connect (pCHAR pszServerName, pCHAR pszUserName, pCHAR pszPassword);
	SQLMETHOD disconnect ();
	SQLMETHOD getSQLCommand (ppSQLCommand ppComm);
	SQLMETHOD getSQLMetaData (ppSQLMetaData ppMeta);
	SQLMETHOD setOption (eSQLConnectOption eCOption, INT32 lValue);
	SQLMETHOD getOption (eSQLConnectOption eCOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength);
	SQLMETHOD beginTransaction (UINT32 ulTransDesc);
	SQLMETHOD commit (UINT32 ulTransDesc);
	SQLMETHOD rollback (UINT32 ulTransDesc);

// IInformixConnection
public:
    METHOD(sqlca_s *) get_sqlca ();
};


/////////////////////////////////////////////////////////////////////////////
// CInformixCommand - SQLCommand implementation for Informix

class CInformixCommand : public CErroredImpl<SQLCommand>
{
private:
	CInformixConnection *m_pConnection;
	
	// dbExpress command options
	INT32 m_cRowset;        // eCommRowsetSize
	INT32 m_cbBlob;         // eCommBlobSize
	BOOL m_bBlockRead;      // eCommBlockRead
	                        // eCommBlockWrite
	                        // eCommParamCount
	ifx_cursor_t *m_pStmt;  // eCommNativeHandle 
	                        // eCommCursorName
	                        // eCommStoredProc
	                        // eCommSQLDialect
	                        // eCommTransactionID
	                        // eCommPackageName
	                        // eCommTrimChar
	                        // eCommQualifiedName
	                        // eCommCatalogName
	                        // eCommSchemaName
	                        // eCommObjectName
	                        // eCommQuotedObjectName

	ifx_sqlda_t *m_pDesc;
	INT32 m_lRowsAffected;

public:
	CInformixCommand ();
	virtual ~CInformixCommand ();
	CInformixConnection * getConnection ();
	void setConnection (CInformixConnection *pConnection);

	ifx_cursor_t * getStmt ();
	ifx_sqlda_t * getDesc ();

	SQLResult checkCurrent ();
	SQLResult checkDormant (SQLResult sqlresult);

	SQLResult unprepare ();

	INT32 getBlobSize ();

// SQLCommand
public:
	SQLMETHOD setOption (eSQLCommandOption eSOption, INT32 lValue);
	SQLMETHOD getOption (eSQLCommandOption eSOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength);
	SQLMETHOD setParameter (UINT16 uParameterNumber, UINT16 uChildPos, STMTParamType ePType, UINT16 uLogType, UINT16 uSubType, INT32 lMaxPrecision, INT32 lMaxScale, UINT32 ulLength, pVOID pBuffer, BOOL bIsNull);
	SQLMETHOD getParameter (UINT16 uParameterNumber, UINT16 uChildPos, pVOID pData, UINT32 ulLength, pINT32 plInd);
	SQLMETHOD prepare (pCHAR pszSQL, UINT16 uParamCount);
	SQLMETHOD execute (ppSQLCursor ppCur);
	SQLMETHOD executeImmediate (pCHAR pszSQL, ppSQLCursor ppCur);
	SQLMETHOD getNextCursor (ppSQLCursor ppCur);
	SQLMETHOD getRowsAffected (pINT32 plRows);
	SQLMETHOD close ();
};


/////////////////////////////////////////////////////////////////////////////
// CInformixCursor - SQLCursor implementation for Informix

#define MAX_COLUMN_NAME_BUFFER 31  // restricted by dbExpress

struct ColumnDesc
{
	CHAR szName[MAX_COLUMN_NAME_BUFFER];
	UINT16 uLogType;
	UINT16 uSubType;
	INT16 iPrecision;
	INT16 iScale;
	BOOL bNullable;
	BOOL bAutoIncrement;
	BOOL bReadOnly;
	BOOL bSearchable;
	BOOL bBlobExactSize;
};

class CInformixCursor : public CErroredImpl<SQLCursor>
{
private:
	CInformixCommand *m_pCommand;
	ifx_cursor_t *m_pCurs;
	ifx_sqlda_t *m_pDesc;
	ColumnDesc *m_pColumns;
	int m_cautolocs;
	loc_t **m_ppautolocs;

	// dbExpress cursor options
	// eCurObjectAttrName
	// eCurObjectTypeName
	// eCurParentFieldID

	SQLResult describeColumn (char *pszDesc, int col_num, ifx_sqlvar_t *pvar, ColumnDesc *pCol);
	SQLResult loadColumns ();

public:
	CInformixCursor ();
	virtual ~CInformixCursor ();
	SQLResult setCommand (CInformixCommand *pCommand);

	SQLResult checkCurrent ();
	SQLResult checkDormant (SQLResult sqlresult);

	ifx_sqlvar_t * getFields ();

// SQLCommand
public:
	SQLMETHOD setOption (eSQLCursorOption eCurOption, INT32 lValue);
	SQLMETHOD getOption (eSQLCursorOption eCurOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength);
	SQLMETHOD getColumnCount (pUINT16 puColumns);
	SQLMETHOD getColumnNameLength (UINT16 uColumnNumber, pUINT16 puLen);
	SQLMETHOD getColumnName (UINT16 uColumnNumber, pCHAR pColumnName);
	SQLMETHOD getColumnType (UINT16 uColumnNumber, pUINT16 puLogType, pUINT16 puSubType);
	SQLMETHOD getColumnLength (UINT16 uColumnNumber, pUINT32 pulLength);
	SQLMETHOD getColumnPrecision (UINT16 uColumnNumber, pINT16 piPrecision);
	SQLMETHOD getColumnScale (UINT16 uColumnNumber, pINT16 piScale);
	SQLMETHOD isNullable (UINT16 uColumnNumber, pBOOL pbNullable);
	SQLMETHOD isAutoIncrement (UINT16 uColumnNumber, pBOOL pbAutoIncrement);
	SQLMETHOD isReadOnly (UINT16 uColumnNumber, pBOOL pbReadOnly);
	SQLMETHOD isSearchable (UINT16 uColumnNumber, pBOOL pbSearchable);
	SQLMETHOD isBlobSizeExact (UINT16 uColumnNumber, pBOOL pbBlobExactSize);
	SQLMETHOD next ();
	SQLMETHOD getString (UINT16 uColumnNumber, pCHAR pData, pBOOL pbIsNull);
	SQLMETHOD getShort (UINT16 uColumnNumber, pINT16 pData, pBOOL pbIsNull);
	SQLMETHOD getLong (UINT16 uColumnNumber, pINT32 pData, pBOOL pbIsNull);
	SQLMETHOD getDouble (UINT16 uColumnNumber, pDFLOAT pData, pBOOL pbIsNull);
	SQLMETHOD getBcd (UINT16 uColumnNumber, pFMTBcd pData, pBOOL pbIsNull);
	SQLMETHOD getTimeStamp (UINT16 uColumnNumber, pCTIMESTAMP pData, pBOOL pbIsNull);
	SQLMETHOD getTime (UINT16 uColumnNumber, pINT32 pData, pBOOL pbIsNull);
	SQLMETHOD getDate (UINT16 uColumnNumber, pINT32 pData, pBOOL pbIsNull);
	SQLMETHOD getBytes (UINT16 uColumnNumber, pBYTE pData, pBOOL pbIsNull);
	SQLMETHOD getBlobSize (UINT16 uColumnNumber, pUINT32 pulLength, pBOOL pbIsNull);
	SQLMETHOD getBlob (UINT16 uColumnNumber, pVOID pData, pBOOL pbIsNull, UINT32 ulLength);
};


/////////////////////////////////////////////////////////////////////////////
// CInformixMetaData - SQLMetaData implementation for Informix

class CInformixMetaDataCursor : public CInformixCursor
{
public:
	SQLMETHOD getString (UINT16 uColumnNumber, pCHAR pData, pBOOL pbIsNull);
};

const char * rgszObjectSQL[] =
{
// eObjTypeDatabase
0,

// eObjTypeDataType
0,

// eObjTypeTable
"select \
	tabid recno, \
	' ' catalog_name, \
	owner schema_name, \
	tabname object_name \
from \
	informix.systables \
where \
	tabtype = 'T'",

// eObjTypeView
"select \
	tabid recno, \
	' ' catalog_name, \
	owner schema_name, \
	tabname object_name \
from \
	informix.systables \
where \
	tabtype = 'V'",

// eObjTypeSynonym
"select \
	tabid recno, \
	' ' catalog_name, \
	owner schema_name, \
	tabname object_name \
from \
	informix.systables \
where \
	tabtype = 'S'",

// eObjTypeProcedure
"select \
	procid recno, \
	' ' catalog_name, \
	owner schema_name, \
	procname object_name \
from \
	informix.sysprocedures",

// eObjTypeUser
"select \
	rowid recno, \
	' ' catalog_name, \
	' ' schema_name, \
	username object_name \
from \
	informix.sysusers",

// eObjTypeRole
0,

// eObjTypeUDT
0
};

const char * szTablesSQL =
"select \
	tabid recno, \
	' ' catalog_name, \
	owner schema_name, \
	tabname table_name, \
	-1 table_type, \
	tabtype \
from \
	informix.systables \
where \
	tabtype in ('T', 'V', 'S') \
	and tabname like ?";

const int TablesSQL_recno = 0;
const int TablesSQL_catalog_name = 1;
const int TablesSQL_schema_name = 2;
const int TablesSQL_table_name = 3;
const int TablesSQL_table_type = 4;
const int TablesSQL_tabtype = 5;

class CInformixTablesCursor : public CInformixMetaDataCursor
{
public:
	SQLMETHOD next ();
};

const char * szProceduresSQL =
"select \
	procid recno, \
	' ' catalog_name, \
	owner schema_name, \
	procname proc_name, \
	1 proc_type, \
	numargs in_params, \
	0 out_params \
from \
	informix.sysprocedures \
where \
	procname like ?";

const char * szColumnsSQL =
"select \
	sc.rowid recno, \
	' ' catalog_name, \
	st.owner schema_name, \
	st.tabname table_name, \
	sc.colname column_name, \
	sc.colno column_position, \
	0 column_type, \
	0 column_datatype, \
	' ' column_typename, \
	0 column_subtype, \
	0 column_precision, \
	0 column_scale, \
	sc.collength column_length, \
	0 column_nullable, \
	sc.coltype \
from \
	informix.systables st, \
	informix.syscolumns sc \
where \
	st.tabname like ? \
	and sc.tabid = st.tabid \
	and sc.colname like ? \
order by 3, 4, 5";

const int ColumnsSQL_recno = 0;
const int ColumnsSQL_catalog_name = 1;
const int ColumnsSQL_schema_name = 2;
const int ColumnsSQL_table_name = 3;
const int ColumnsSQL_column_name = 4;
const int ColumnsSQL_column_position = 5;
const int ColumnsSQL_column_type = 6;
const int ColumnsSQL_column_datatype = 7;
const int ColumnsSQL_column_typename = 8;
const int ColumnsSQL_column_subtype = 9;
const int ColumnsSQL_column_precision = 10;
const int ColumnsSQL_column_scale = 11;
const int ColumnsSQL_column_length = 12;
const int ColumnsSQL_column_nullable = 13;
const int ColumnsSQL_coltype = 14;

class CInformixColumnsCursor : public CInformixMetaDataCursor
{
public:
	SQLMETHOD next ();
};

const char * szColumnsShortSQL =
"select \
	st.tabid, \
	sc.colno, \
	sc.colname \
from \
	informix.systables st, \
	informix.syscolumns sc \
where \
	st.tabname like ? \
	and sc.tabid = st.tabid ";

const int ColumnsShortSQL_tabid = 0;
const int ColumnsShortSQL_colno = 1;
const int ColumnsShortSQL_colname = 2;

const char * rgszIndicesSQL[] =
{
// Informix-SE compatible SQL
"select \
	-1 recno, \
	' ' catalog_name, \
	si.owner schema_name, \
	st.tabname table_name, \
	si.idxname index_name, \
	scon.idxname pkey_name, \
	' ' column_name, \
	0 column_position, \
	0 index_type, \
	' ' sort_order, \
	' ' filter, \
	si.tabid, \
	si.idxtype, \
	si.part1, si.part2, si.part3, si.part4, si.part5, si.part6, si.part7, si.part8 \
from \
	informix.systables st, \
	informix.sysindexes si, \
	outer informix.sysconstraints scon \
where \
	st.tabname like ? \
	and si.tabid = st.tabid \
	and scon.tabid = st.tabid \
	and scon.constrtype = 'P' \
order by 3, 4, 5",
// for others Informix servers
"select \
	-1 recno, \
	' ' catalog_name, \
	si.owner schema_name, \
	st.tabname table_name, \
	si.idxname index_name, \
	scon.idxname pkey_name, \
	' ' column_name, \
	0 column_position, \
	0 index_type, \
	' ' sort_order, \
	' ' filter, \
	si.tabid, \
	si.idxtype, \
	si.part1, si.part2, si.part3, si.part4, si.part5, si.part6, si.part7, si.part8, \
	si.part9, si.part10, si.part11, si.part12, si.part13, si.part14, si.part15, si.part16 \
from \
	informix.systables st, \
	informix.sysindexes si, \
	outer informix.sysconstraints scon \
where \
	st.tabname like ? \
	and si.tabid = st.tabid \
	and scon.tabid = st.tabid \
	and scon.constrtype = 'P' \
order by 3, 4, 5"
};

const int IndicesSQL_recno = 0;
const int IndicesSQL_catalog_name = 1;
const int IndicesSQL_schema_name = 2;
const int IndicesSQL_table_name = 3;
const int IndicesSQL_index_name = 4;
const int IndicesSQL_pkey_name = 5;
const int IndicesSQL_column_name = 6;
const int IndicesSQL_column_position = 7;
const int IndicesSQL_index_type = 8;
const int IndicesSQL_sort_order = 9;
const int IndicesSQL_filter = 10;
const int IndicesSQL_tabid = 11;
const int IndicesSQL_idxtype = 12;
const int IndicesSQL_part_begin = 13;
const int IndicesSQL_part_end[] = { 20, 28 };

class CInformixIndicesCursor : public CInformixMetaDataCursor
{
private:
	INT32 m_recno;
	int m_tabid;
	short m_colno;
	int m_partcol;
	_hash_t<_cstr_t, _cstr_t, compare_cstr> *m_phashTabCol;
	UINT32 m_index_type_filter;
	int m_IndicesSQL_part_end;
public:
	CInformixIndicesCursor ();
	virtual ~CInformixIndicesCursor ();
	SQLResult initialize (CInformixMetaDataCursor *pColumnsCursor, UINT32 index_type_filter,
		int IndicesSQL_part_end);
	SQLMETHOD next ();
};

class CInformixMetaData : public CUnknownImpl<SQLMetaData>
{
private:
	CInformixCommand m_Command;
	CInformixConnection *m_pConnection;

	// dbExpress metadata options
	_cstr_t m_strDatabase;  // eMetaCatalogName
	_cstr_t m_strUser;      // eMetaSchemaName
	                        // eMetaDatabaseName
	                        // eMetaDatabaseVersion
	                        // eMetaTransactionIsoLevel
	                        // eMetaSupportsTransaction
	                        // eMetaMaxObjectNameLength
	                        // eMetaMaxColumnsInTable
	                        // eMetaMaxColumnsInSelect
	                        // eMetaMaxRowSize
	                        // eMetaMaxSQLLength
	                        // eMetaObjectQuoteChar
	                        // eMetaSQLEscapeChar
	                        // eMetaProcSupportsCursor
	                        // eMetaProcSupportsCursors
	                        // eMetaSupportsTransactions
	                        // eMetaPackageName

	struct ParamDesc
	{
		UINT16 uLogType;
		UINT16 uSubType;
		UINT32 ulLength;
		pVOID pBuffer;
	};

	SQLResult getInfo (pCHAR pszSQL, int cParams, ParamDesc* pParams, CInformixMetaDataCursor *pCur, ppSQLCursor ppCur);

public:
	virtual ~CInformixMetaData() {}
	void setConnection(CInformixConnection *pConnection);

// SQLMetaData
public:
	SQLMETHOD setOption (eSQLMetaDataOption eDOption, INT32 lValue);
	SQLMETHOD getOption (eSQLMetaDataOption eDOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength);
	SQLMETHOD getObjectList (eSQLObjectType eObjType, ppSQLCursor ppCur);
	SQLMETHOD getTables (pCHAR pszTableName, UINT32 uTableType, ppSQLCursor ppCur);
	SQLMETHOD getProcedures (pCHAR pszProcName, UINT32 uProcType, ppSQLCursor ppCur);
	SQLMETHOD getColumns (pCHAR pszTableName, pCHAR pszColumnName, UINT32 uColType, ppSQLCursor ppCur);
	SQLMETHOD getProcedureParams (pCHAR pszProcName, pCHAR pszParamName, ppSQLCursor ppCur);
	SQLMETHOD getIndices (pCHAR pszTableName, UINT32 uIndexType, ppSQLCursor ppCur);
	SQLMETHOD getErrorMessage (pBYTE pszError);
	SQLMETHOD getErrorMessageLen (pUINT16 puErrorLen);
};


/////////////////////////////////////////////////////////////////////////////
// Miscelaneous routines

void trimString (char *lpsz);
void free_hostvar (ifx_sqlvar_t *pvar, bool local_lob);
void clear_hostvar (ifx_sqlvar_t *pvar, bool local_lob);
void free_sqlda (ifx_sqlda_t *sqlda, bool local_lob);


#endif // _IFXSQL_HPP
